package it.unicam.cs.cosy.bioshape.core.util;

import hermesV3.services.Space.SpaceManager;
import hermesV3.util.HermesLogger;
import it.unicam.cs.cosy.bioshape.core.behaviours.ActiveSite;
import it.unicam.cs.cosy.bioshape.core.behaviours.Behaviour;
import it.unicam.cs.cosy.bioshape.core.behaviours.BehaviourException;
import it.unicam.cs.cosy.bioshape.core.behaviours.BehaviourTuple;
import it.unicam.cs.cosy.bioshape.core.behaviours.Nil;
import it.unicam.cs.cosy.bioshape.core.behaviours.SCParser;
import it.unicam.cs.cosy.bioshape.core.components.EntityData;
import it.unicam.cs.cosy.bioshape.core.components.ProtoEntity;
import it.unicam.cs.cosy.bioshape.core.components.equilibriums.EquilibriumCondition;
import it.unicam.cs.cosy.bioshape.core.components.movers.EntityDriver;
import it.unicam.cs.cosy.bioshape.core.components.services.Service;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Activesite;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Atomicshape;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Atomicshape.Face;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Atomicshape.Vertex;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Bounds;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Compoundshape;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Network;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Network.Process.Placeholders;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Network.Process.Placeholders.Placeholder;
import it.unicam.cs.cosy.bioshape.core.util.Simulation.Scale;
import it.unicam.cs.cosy.bioshape.core.util.Transs.Trans;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Serializable;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.measure.quantity.Duration;
import javax.measure.quantity.Length;
import javax.measure.unit.Unit;
import javax.media.j3d.Transform3D;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import javax.xml.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.SAXException;

import vclip.PolyTree;

/**
 * Reads the simulation data from the XML input on the basis of the JAXB API.
 * Creates support data structure for the set up of the simulation and the
 * visualization tool.
 * 
 * @author Federico Buti
 */
public class XmlReader implements Serializable {

    /** auto-generated serial ID */
    private static final long serialVersionUID = -2721685210473763082L;

    // root node
    private Simulation simulation = null;

    // data
    private Map<String, PolyTree> library = new HashMap<String, PolyTree>();

    /** @return the library */
    public Map<String, PolyTree> getLibrary() {
        return library;
    }

    /** @return the activeSites */
    public Map<String, ActiveSite> getActiveSites() {
        return activeSites;
    }

    /** @return the behaviours */
    public Map<String, Behaviour> getBehaviours() {
        return behaviours;
    }

    /** @return the protos */
    public ArrayList<ProtoEntity> getProtos() {
        return protos;
    }

    /** @return the services */
    public ArrayList<Service> getServices() {
        return services;
    }

    /** @return the spatialDelta */
    public double getSpatialDelta() {
        return spatialDelta;
    }

    /** @return the maxrotation */
    public double getMaxrotation() {
        return maxrotation;
    }

    /** @return the maxoutputfilesize */
    public double getMaxoutputfilesize() {
        return maxoutputfilesize;
    }

    /** @return the temporaldelta */
    public double getTemporaldelta() {
        return temporaldelta;
    }

    /** @return the scale */
    public it.unicam.cs.cosy.bioshape.core.components.Scale getScale() {
        return scale;
    }

    /** @return the eq */
    public EquilibriumCondition getEq() {
        return eq;
    }

    /** @return the status indicating the result of the input reading. */
    public String getStatus() {
        return status;
    }

    private Map<String, ActiveSite> activeSites = new HashMap<String, ActiveSite>();

    private Map<String, Behaviour> behaviours = new HashMap<String, Behaviour>();

    private ArrayList<ProtoEntity> protos = new ArrayList<ProtoEntity>();

    private ArrayList<Service> services = new ArrayList<Service>();

    private double spatialDelta;

    private double maxrotation;

    private double maxoutputfilesize;

    private double temporaldelta;

    private final static String EQUILIBRIUM = "it.unicam.cs.cosy.bioshape.core.components.equilibriums.";

    private final static String SERVICES = "it.unicam.cs.cosy.bioshape.core.components.services.";

    private final static String MOVERS = "it.unicam.cs.cosy.bioshape.core.components.movers.";

    private it.unicam.cs.cosy.bioshape.core.components.Scale scale;

    private EquilibriumCondition eq;

    private String status = "Parsing successful";

    private String globalIP;

    private int locals;

    private int cuttingAxis = SpaceManager.SPLITX; // default is X splitting

    private Point3d[] simulationBounds = new Point3d[2];

    private ArrayList<ObjShape> objShapes = new ArrayList<ObjShape>();

    /**
     * @return the objShapes
     */
    public ArrayList<ObjShape> getObjShapes() {
        return objShapes;
    }

    private File file;

    /**
     * @return the offset
     */
    public double getOffset() {
        return offset;
    }

    private double offset;

    /** @return the cuttingAxis */
    public int getCuttingAxis() {
        return cuttingAxis;
    }

    // TODO VA PREVISTO IL CONTROLLO A LIVELLO DI SHAPE CHE IL BEHAVIOUR
    // DEFINITO NON CONTENGA CHANNELS NON PRESENTI NELLA SHAPE. QUESTO SI PUO'
    // FARE CON UN METODO CHE SI SCORRE SHAPE E BEHAVIOUR E NE VERIFICA LA
    // COMPATIBILITA'. TALE METODO E', ANCORA UNA VOLTA, IMPLEMENTATO IN QUESTA
    // CLASSE.
    /**
     * Opens the XML input file and validate it against the XSD schema.
     * 
     * @param file
     *            XML file to parse. The XML Schema
     *            {@code BioShapeInputSchema.xsd} is used for validation.
     * @throws JAXBException
     *             if an error was encountered while creating the JAXB-related
     *             objects.
     * @throws SAXException
     *             If a SAX error occurs during parsing.
     * @throws FileNotFoundException
     *             if the file does not exist, is a directory rather than a
     *             regular file, or for some other reason cannot be opened for
     *             reading.
     */
    public XmlReader(File file) throws JAXBException, SAXException,
            FileNotFoundException {
        this.file = file;
        JAXBContext jc = null;
        Unmarshaller u = null;
        jc = JAXBContext.newInstance(Simulation.class);

        u = jc.createUnmarshaller();

        SchemaFactory schemaFactory = SchemaFactory
                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = schemaFactory.newSchema(new File(
                "BioShapeInputSchema.xsd"));
        u.setSchema(schema);
        u.setEventHandler(new ValidationEventHandler() {
            @Override
            public boolean handleEvent(ValidationEvent ve) {
                int index = ve.getMessage().indexOf(':');
                String msg = index == -1 ? ve.getMessage() : ve.getMessage()
                        .substring(index + 2);
                if (ve.getLinkedException().getClass() == NumberFormatException.class) {
                    msg = "The value specified at line "
                            + ve.getLocator().getLineNumber() + " and column "
                            + ve.getLocator().getColumnNumber() + " is n"
                            + ve.getLinkedException().getMessage().substring(1);
                }
                HermesLogger.log(HermesLogger.ERROR, "XMLReader - " + msg);
                return false;
            }
        });
        simulation = (Simulation) u.unmarshal(new FileInputStream(file));
    }

    /**
     * @return the file
     */
    public File getFile() {
        return file;
    }

    private boolean readNetwork(Network network) {
        for (Simulation.Network.Process process : network.process) {
            int cardinality = process.cardinality.intValue();
            // PolyTree polyTree = library.get(process.shape);
            // if (polyTree == null)
            // return false;
            // Luca: per sicurezza
            // polyTree.buildAllBoundingHulls(PolyTree.CONVEX_HULL);
            Behaviour behaviour = behaviours.get(process.behaviour);
            double mass = process.mass;
            double perceptionRadius = process.percradius;
            // create proto-entity
            ProtoEntity protoEntity = new EntityData(process.shape, library,
                    mass, behaviour, perceptionRadius, cardinality);
            protos.add(protoEntity);
            // insert the centers and the orientations
            Placeholders placeholders = process.placeholders;
            if (placeholders != null) { // no random setting
                if (placeholders.placeholder.size() != process.cardinality
                        .intValue()) {
                    status = "The cardinality of a defined process ("
                            + process.cardinality
                            + ") is different from the number "
                            + "of its placeholders ("
                            + placeholders.placeholder.size() + ")";
                    return false;
                }
                for (Placeholder placeholder : placeholders.placeholder) {
                    Vector3d translationVector = null, rotationPseudovector = null;
                    translationVector = new Vector3d(placeholder.trans.x,
                            placeholder.trans.y, placeholder.trans.z);
                    if (placeholder.rotx != null)
                        rotationPseudovector = new Vector3d(
                                placeholder.rotx.doubleValue(),
                                placeholder.roty.doubleValue(),
                                placeholder.rotz.doubleValue());
                    protoEntity.addCenterAndRotation(translationVector,
                            rotationPseudovector);
                }
            }
            // add other information for the ShapedEntity instantiation
            //
            // EntityDriver driver = (EntityDriver)
            // getInstanceOfClassFromParameters(
            // process.driver, MOVERS);
            protoEntity.setInputDriverInformation(process.driver, MOVERS);
            protoEntity.setEntityClass(process.clazz);
            protoEntity.setBoundsType(process.bounds);
        }
        return true;
    }

    /**
     * Parses the XML file validated in the constructor. Information read is
     * collected in support data structure. The latter can be used to set-up a
     * simulation by the {@code GlobalCoordinator} and the {@code SpaceManager}.
     * 
     * @return {@code true} if the file is successfully parsed, {@code false}
     *         otherwise.
     */
    public boolean parseInput() {
        // read content
        List<JAXBElement<? extends Serializable>> list = simulation
                .getActivesiteOrAtomicshapeOrBehaviour();

        // read basic elements...
        if (!readBasicElements(list)) {
            HermesLogger.log(HermesLogger.ERROR, "XMLReader - " + status);
            return false;
        }

        // reads COMPOUND SHAPES
        for (JAXBElement<?> e : list) {
            if (e.getName().toString().equals("compoundshape"))
                if (!readCompoundShape((Compoundshape) e.getValue())) {
                    HermesLogger.log(HermesLogger.ERROR, "XMLReader - "
                            + status);
                    return false;
                }
        }

        // read BEHAVIOURS
        StringBuilder b = new StringBuilder();
        for (JAXBElement<?> e : list) {
            if (e.getName().toString().equals("behaviour")) {
                b.append(e.getValue() + ";");
            }
        }
        if (!readBehaviour(b.toString())) {
            HermesLogger.log(HermesLogger.ERROR, this.getClass()
                    .getSimpleName()
                    + " - Please consider to review"
                    + " your input.");
            return false;
        }

        // reads 3D PROCESSES
        for (JAXBElement<?> e : list) {
            if (!e.getName().toString().equals("network"))
                continue;
            if (!readNetwork((Network) e.getValue())) {
                HermesLogger.log(HermesLogger.ERROR, this.getClass()
                        .getSimpleName() + " - " + status);
                return false;
            }

        }
        return true;
    }

    // private boolean readCompoundShape(Compoundshape compoundShape) {
    // ObjShape objShape = new ObjShape(compoundShape.name);
    // StringBuilder compoundShapeInVclipFormat = new StringBuilder();
    // compoundShapeInVclipFormat.append("compound");
    // compoundShapeInVclipFormat.append(" " + compoundShape.name);
    // PolyTree compoundPoly = null;
    // // TODO finire!!! LUCA
    // // transformationsOfComponents sono o traslazione o rotazioni, quelle
    // // sul file xml per
    // // le componenti della compound shapes
    // ArrayList<Transform3D> transformationsOfComponents = new
    // ArrayList<Transform3D>();
    // compoundShapeInVclipFormat.append("  [\n");
    // Transform3D trans = new Transform3D();
    // Transform3D temp = new Transform3D();
    // for (Object o : compoundShape.transformAndShapename) {
    // temp.setIdentity();
    // if (o instanceof Transs) {
    // // o is a transformation, either translation or rotation
    // compoundShapeInVclipFormat.append("[");
    // for (JAXBElement<?> t : ((Transs) o).getTransOrRotxOrRoty()) {
    // if (t.getValue() instanceof Trans) {
    // // case in which t is a translation
    // Trans tt = (Trans) t.getValue();
    // compoundShapeInVclipFormat.append("trans " + tt.x
    // + "  " + tt.y + "  " + tt.z + "  ");
    // updateTransformation(trans, tt.x, tt.y, tt.z);
    // Transform3D app = new Transform3D();
    // app.setTranslation(new Vector3d(new double[] { tt.x,
    // tt.y, tt.z }));
    // transformationsOfComponents.add(app);
    // } else {
    // // case in which t is a rotation along one of the axes
    // compoundShapeInVclipFormat.append(t.getName() + " "
    // + t.getValue() + "  ");
    // updateTransformation(trans, temp, t.getName()
    // .toString(),
    // ((Double) t.getValue()).doubleValue());
    // Transform3D app = new Transform3D();
    // if (t.getName().toString().equals("rotx"))
    // app.rotX(((Double) t.getValue()).doubleValue());
    // else if (t.getName().toString().equals("roty"))
    // app.rotY(((Double) t.getValue()).doubleValue());
    // else if (t.getName().toString().equals("roty"))
    // app.rotZ(((Double) t.getValue()).doubleValue());
    // else
    // System.out
    // .println("ERRORE: Nel file di input, le componenti della compound shape "
    // + compoundShape.name
    // + " hanno una trasformazione né rotx, né roty, né rotz");
    // transformationsOfComponents.add(app);
    // }
    // }
    // compoundShapeInVclipFormat.append("]");
    // } else {
    // // o is the shape name to which the previously transformations
    // // must be applied
    // String shapeName = (String) o;
    // compoundShapeInVclipFormat.append("  " + shapeName + "\n");
    // ObjShape ref = getShape(shapeName);
    // if (ref == null) {
    // status = "The shape \"" + shapeName
    // + "\" is not available for "
    // + "the compound shape \"" + compoundShape.name
    // + "\"";
    // return false;
    // }
    // objShape.addOtherShape(getShape(shapeName), trans);
    //
    // if (transformationsOfComponents.size() == 0)
    // // There are NOT transformations for the current atomic
    // // shape
    // // that is going to be added to the compound shape
    // if (compoundPoly == null)
    // // Create a polytree with the name of the compound shape
    // // and, as initial polytree, the one corresponding to
    // // shapeName
    // compoundPoly = new PolyTree(compoundShape.name,
    // library.get(shapeName));
    // else {
    // // Add the current shapeName to the compound shape
    // compoundPoly.addComponent(shapeName,
    // library.get(shapeName));
    // }
    // else {
    // // There are transformations for the current atomic shape
    // // that is going to be added to the compound shape
    // // Start from identity
    // Transform3D app = new Transform3D();
    // for (int i = 0; i < transformationsOfComponents.size(); i++) {
    // // Compose transformations in the same order in which
    // // they
    // // are given in the input
    // app.mul(transformationsOfComponents.get(i));
    // }
    // // Insert the new component in the PolyTree
    // Matrix4d m = new Matrix4d();
    // app.get(m);
    // if (compoundPoly == null) {
    // compoundPoly = new PolyTree(compoundShape.name,
    // library.get(shapeName));
    // compoundPoly.setTransform(m);
    // } else {
    // compoundPoly.addComponent(shapeName,
    // library.get(shapeName), m);
    // }
    // }
    // trans.setIdentity();
    // // Riazzero
    // transformationsOfComponents = new ArrayList<Transform3D>();
    // }
    // }
    // // Ma l'ha messi i trans e i rotx roty o rotz??
    // compoundShapeInVclipFormat.append("]");
    // // TODO è il caso di toglierlo e di costruire una objShape dal
    // // compoundPoly
    // // non lo so dipende se è compound dall'inizio oppure ha fatto il bind e
    // // si
    // // può splittare
    // objShapes.add(objShape);
    // // Potrei usare scanLibrary, ma ho costruito il polytree e quindi no
    //
    // // TODO fare generare pure il file in formato vclip da
    // // compoundShapeInVclipFormat, in questo caso
    // // controllare che inserisca le stringhe giuste
    //
    // // try {
    // // PolyTree.scanLibrary(
    // // new StreamTokenizer(new
    // // StringReader(compoundShapeInVclipFormat.toString())),
    // // library, true);
    //
    // // creo l'hull obbligatoriamente
    // compoundPoly.buildBoundingHull();
    // library.put(compoundShape.name, compoundPoly);
    //
    // // } catch (IOException e) {
    // // status = e.getMessage();
    // // return false;
    // // }
    // return true;
    // }

    private boolean readCompoundShape(Compoundshape c) {
        ObjShape objShape = new ObjShape(c.name);
        StringBuilder s = new StringBuilder();
        s.append("compound");
        s.append(" " + c.name);
        s.append("  [\n");
        Transform3D trans = new Transform3D();
        Transform3D temp = new Transform3D();
        for (Object o : c.transformAndShapename) {

            temp.setIdentity();
            if (o instanceof Transs) {
                s.append("[");
                for (JAXBElement<?> t : ((Transs) o).getTransOrRotxOrRoty()) {
                    if (t.getValue() instanceof Trans) {
                        Trans tt = (Trans) t.getValue();
                        s.append("trans " + tt.x + "  " + tt.y + "  " + tt.z
                                + "  ");
                        updateTransformation(trans, tt.x, tt.y, tt.z);
                    } else {
                        s.append(t.getName() + " " + t.getValue() + "  ");
                        updateTransformation(trans, temp, t.getName()
                                .toString(),
                                ((Double) t.getValue()).doubleValue());
                    }
                }
                s.append("]");
            } else {
                s.append("  " + ((String) o) + "\n");
                ObjShape ref = getShape(((String) o));
                if (ref == null) {
                    status = "The shape \"" + o + "\" is not available for "
                            + "the compound shape \"" + c.name + "\"";
                    return false;
                }
                objShape.addOtherShape(getShape(((String) o)), trans);
                trans.setIdentity();
            }
        }
        s.append("]");
        objShapes.add(objShape);
        try {
            PolyTree.scanLibrary(
                    new StreamTokenizer(new StringReader(s.toString())),
                    library, true);
            // System.out.println(
            // "XMLReader: read the following library of polytrees:\n"
            // + library);
        } catch (IOException e) {
            status = e.getMessage();
            return false;
        }
        return true;
    }

    ObjShape getShape(String string) {
        for (ObjShape objShape : objShapes)
            if (objShape.getName().equals(string))
                return objShape;
        return null;
    }

    private void updateTransformation(Transform3D trans, double x, double y,
            double z) {
        Vector3d v = new Vector3d();
        trans.get(v);
        v.x += x;
        v.y += y;
        v.z += z;
        trans.setTranslation(v);
    }

    private void updateTransformation(Transform3D trans, Transform3D temp,
            String axis, double angle) {
        // Vector3d tempV = new Vector3d();
        // trans.get(tempV);
        // trans.setTranslation(new Vector3d(0, 0, 0));
        if (axis.equals("rotx"))
            temp.rotX(Math.toRadians(angle));
        else if (axis.equals("roty"))
            temp.rotY(Math.toRadians(angle));
        else
            temp.rotZ(Math.toRadians(angle));
        //
        // switch (axis) {
        // case "rotx":
        // temp.rotX(Math.toRadians(angle));
        // break;
        // case "roty":
        // temp.rotY(Math.toRadians(angle));
        // break;
        // case "rotz":
        // temp.rotZ(Math.toRadians(angle));
        // }
        //
        trans.mul(temp, trans);
        // trans.setTranslation(tempV);
        temp.setIdentity();
    }

    // Constants needed for the big switch of the next method
    private static final int ACTIVESITE = 0;

    private static final int ATOMICSHAPE = 1;

    private static final int SHAPEFILE = 2;

    private static final int EQUILIBRIUM_BIS = 3; // duplicate name

    private static final int SERVICES_BIS = 4; // duplicate name

    private static final int SCALE = 5;

    private static final int SPATIALDELTA = 6;

    private static final int MAXROTATION = 7;

    private static final int MAXOUTPUTFILESIZE = 8;

    private static final int TEMPORALDELTA = 9;

    private static final int GLOBALIP = 10;

    private static final int LOCALS = 11;

    private static final int CUTTINGAXIS = 12;

    private static final int BOUNDS = 13;

    @SuppressWarnings("unchecked")
    private boolean readBasicElements(
            List<JAXBElement<? extends Serializable>> list) {
        for (JAXBElement<?> e : list) {
            final String tag = e.getName().toString();
            int tagInt = -1;
            if (tag.equals("activesite"))
                tagInt = ACTIVESITE;
            else if (tag.equals("atomicshape"))
                tagInt = ATOMICSHAPE;
            else if (tag.equals("shapefile"))
                tagInt = SHAPEFILE;
            else if (tag.equals("equilibrium"))
                tagInt = EQUILIBRIUM_BIS;
            else if (tag.equals("services"))
                tagInt = SERVICES_BIS;
            else if (tag.equals("scale"))
                tagInt = SCALE;
            else if (tag.equals("spatialdelta"))
                tagInt = SPATIALDELTA;
            else if (tag.equals("maxrotation"))
                tagInt = MAXROTATION;
            else if (tag.equals("maxoutputfilesize"))
                tagInt = MAXOUTPUTFILESIZE;
            else if (tag.equals("temporaldelta"))
                tagInt = TEMPORALDELTA;
            else if (tag.equals("globalIP"))
                tagInt = GLOBALIP;
            else if (tag.equals("locals"))
                tagInt = LOCALS;
            else if (tag.equals("cuttingaxis"))
                tagInt = CUTTINGAXIS;
            else if (tag.equals("bounds"))
                tagInt = BOUNDS;

            switch (tagInt) {

            case ACTIVESITE:
                if (!readActiveSite((JAXBElement<Simulation.Activesite>) e))
                    return false;
                break;

            case ATOMICSHAPE:
                if (!readAtomicShape((Atomicshape) e.getValue()))
                    return false;
                break;

            case SHAPEFILE:
                if (!readShapeFile((JAXBElement<String>) e))
                    return false;
                break;

            case EQUILIBRIUM_BIS:
                eq = (EquilibriumCondition) getInstanceOfClassFromParameters(
                        (Parameters) e.getValue(), EQUILIBRIUM);
                if (eq == null)
                    return false;
                break;

            case SERVICES_BIS:
                Service sv = (Service) getInstanceOfClassFromParameters(
                        (Parameters) e.getValue(), SERVICES);
                if (sv == null)
                    return false;
                if (!services.contains(sv))
                    services.add(sv);
                break;

            case SCALE:
                Scale s = (Scale) e.getValue();
                Unit<Duration> time = null;
                Unit<Length> space = null;
                try {
                    time = (Unit<Duration>) Unit.valueOf(s.temporal);
                    space = (Unit<Length>) Unit.valueOf(s.spatial);
                } catch (IllegalArgumentException iae) {
                    if (time == null)
                        status = "The string '" + s.temporal
                                + "' is not a valid "
                                + "temporal unit of measure";
                    else
                        status = "The string '" + s.spatial
                                + "' is not a valid "
                                + "spatial unit of measure";
                    return false;
                }
                scale = new it.unicam.cs.cosy.bioshape.core.components.Scale(
                        time, space);
                break;

            case SPATIALDELTA:
                spatialDelta = ((Double) e.getValue()).doubleValue();
                break;

            case MAXROTATION:
                maxrotation = ((Double) e.getValue()).doubleValue();
                break;

            case MAXOUTPUTFILESIZE:
                maxoutputfilesize = ((Double) e.getValue()).doubleValue();
                break;

            case TEMPORALDELTA:
                temporaldelta = ((Double) e.getValue()).doubleValue();
                break;

            case GLOBALIP:
                globalIP = (String) e.getValue();
                break;

            case LOCALS:
                locals = ((Integer) e.getValue()).intValue();
                if (locals <= 0) {
                    status = "'" + locals + "' tag cannot have a negative "
                            + "number. Please specify a positive number of "
                            + "Local Coordinators.";
                    return false;
                }
                break;

            case CUTTINGAXIS:
                String axis = (String) e.getValue();
                if (axis.equals("x"))
                    cuttingAxis = SpaceManager.SPLITX;
                else if (axis.equals("y"))
                    cuttingAxis = SpaceManager.SPLITY;
                else if (axis.equals("z"))
                    cuttingAxis = SpaceManager.SPLITZ;
                else {
                    status = "'" + axis + "' is not a valid cutting axis in "
                            + "tag 'cuttingaxis'.";
                    return false;
                }
                break;

            case BOUNDS:
                Bounds b = (Bounds) e.getValue();
                if (b.min.x >= b.max.x || b.min.y >= b.max.y
                        || b.min.z >= b.max.z) {
                    status = "Simulation bounds not correctly set: the minimum"
                            + " bound MUST be smaller than the maximum one!";
                    return false;
                }
                simulationBounds[0] = new Point3d(b.min.x, b.min.y, b.min.z);
                simulationBounds[1] = new Point3d(b.max.x, b.max.y, b.max.z);
            }
        }
        return true;
    }

    /**
     * @return the simulationBounds
     */
    public Point3d[] getSimulationBounds() {
        return simulationBounds;
    }

    private boolean readActiveSite(JAXBElement<Activesite> e) {
        Activesite a = e.getValue();
        if (activeSites.get(a.name) != null) {
            status = "Duplicated active site for shape " + a.name;
            return false;
        }
        //
        if (a.feature.size() == 0) {
            status = "Empty feature set for shape " + a.name;
            return false;
        }
        // else...
        List<String> listFeatures = new ArrayList<String>();
        for (String f : a.feature) {
            if (!listFeatures.contains(f))
                listFeatures.add(f);
        }
        activeSites.put(a.name, new ActiveSite(a.name, listFeatures));
        return true;
    }

    private boolean readBehaviour(String beh) {
        String temp1 = beh.replace('[', '<');
        String temp2 = temp1.replace(']', '>');
        SCParser behParser = new SCParser(temp2, activeSites);
        if (behParser.parse())
            try {
                behParser.getBehTable().checkAndBuid();
                // doTest(behParser.getBehTable().getRows().get(0).getState());
                for (BehaviourTuple p : behParser.getBehTable().getRows())
                    behaviours.put(p.getName(), p.getState());
                // TODO TOGLIERE LA SEGUENTE PARTE: È TEST!
                // TraceGenerator t = new TraceGenerator();
                // System.out.println("\n\n\n PROCESSES");
                // for (Behaviour b : behaviours.values())
                // t.addConcreteProcess(b);
                // t.generateTraces();
                // System.out.println("\n\n\n");
                // System.exit(1);
                // TODO FINE PARTE TEST
                return true;
            } catch (BehaviourException e) {
                HermesLogger.log(HermesLogger.ERROR, this.getClass()
                        .getSimpleName() + " - " + e.getMessage());
            }
        return false;
    }

    private void doTest(Behaviour state) {
        System.out.println(state);
        System.out.println(state.getLabel());
        if (!(state instanceof Nil))
            doTest(state.getNext());
    }

    private boolean readShapeFile(JAXBElement<String> e) {
        try {
            PolyTree.scanLibrary(
                    new StreamTokenizer(new FileReader(e.getValue())), library,
                    true);
            System.out.println("In readShapeFile - library:\n");
            System.out.println(library);
        } catch (IOException ioe) {
            status = "Error while reading the given shape file." + e.getValue();
            return false;
        }
        return true;
    }

    private boolean readAtomicShape(Atomicshape a) {
        ObjShape objShape = new ObjShape(a.name);
        StringBuilder shapeInVclipFormat = new StringBuilder();
        shapeInVclipFormat.append("atomic");
        shapeInVclipFormat.append(" " + a.name);
        shapeInVclipFormat.append("\n");
        for (Vertex v : a.vertex) {
            shapeInVclipFormat.append(v.name + "\t");
            shapeInVclipFormat.append(v.coordinates.x + "  ");
            shapeInVclipFormat.append(v.coordinates.y + "  ");
            shapeInVclipFormat.append(v.coordinates.z + "  ");
            shapeInVclipFormat.append("\n");
            objShape.addVertex(v.name, v.coordinates.x, v.coordinates.y,
                    v.coordinates.z);
        }
        shapeInVclipFormat.append("*\n");
        for (Face f : a.face) {
            shapeInVclipFormat.append(f.name + "\t");
            for (String v : f.vertex)
                shapeInVclipFormat.append(v + "  ");
            shapeInVclipFormat.append("\n");
            if (!objShape.addFace(f.name, f.vertex)) {
                status = "One of the vertices of face \"" + f.name
                        + "\" is not defined for the atomic shape \"" + a.name
                        + "\"";
                return false;
            }
        }
        shapeInVclipFormat.append("*");
        try {
            // TODO Generare un file nel formato vclip tramite
            // shapeInVclipFormat
            PolyTree.scanLibrary(new StreamTokenizer(new StringReader(
                    shapeInVclipFormat.toString())), library, true);
        } catch (Exception e) {
            status = e.getMessage();
            return false;
        }
        //
        objShapes.add(objShape);
        return true;
    }

    private Object getInstanceOfClassFromParameters(Parameters parameters,
            String path) throws IllegalArgumentException {
        ClassLoader classLoader = XmlReader.class.getClassLoader();
        try {
            // TODO maybe can be powered-up with Reflections library for
            // non-fully qualified name search
            Class<?> classOfTheInstance = Class.forName(path + parameters.name,
                    false, classLoader);
            if (parameters.parameter == null)
                return classOfTheInstance.getConstructor().newInstance();
            // parameters
            Class<?>[] classes = new Class[parameters.parameter.size()];
            Object[] objects = new Object[parameters.parameter.size()];
            for (int i = 0; i < parameters.parameter.size(); i++) {
                classes[i] = Class
                        .forName(parameters.parameter.get(i).formalparameter);
                objects[i] = checkType(classes[i],
                        parameters.parameter.get(i).actualparameter);
            }
            return classOfTheInstance.getConstructor(classes).newInstance(
                    objects);
        } catch (SecurityException e) {
            if (e.getClass().getSimpleName().contains("ClassNotFound"))
                status = "The class '" + parameters.name
                        + "' was not found. Please "
                        + "check your input for errors in path.";
        } catch (Exception e) {
            if (e.getClass().getSimpleName().contains("ClassNotFound"))
                status = "The class '" + parameters.name
                        + "' was not found. Please "
                        + "check your input for errors in path.";
        }
        return null;
    }

    private Object checkType(Class<?> clazz, String actualparameter) {
        if (clazz.getCanonicalName().contains("Double"))
            return new Double(actualparameter);
        if (clazz.getCanonicalName().contains("Integer"))
            return new Integer(actualparameter);
        if (clazz.getCanonicalName().contains("Long"))
            return new Long(actualparameter);
        if (clazz.getCanonicalName().contains("Float"))
            return new Float(actualparameter);
        if (clazz.getCanonicalName().contains("Short"))
            return new Short(actualparameter);
        return actualparameter;
    }

    /**
     * @return the globalIP
     */
    public String getGlobalIP() {
        return globalIP;
    }

    /**
     * @return the locals
     */
    public int getLocals() {
        return locals;
    }

    /**
     * Updates the locals cardinality: if less locals are available during the
     * simulation it must be stored. Important for the visualiser start up.
     * 
     * @param locals
     *            The {@code LocalCoordinator} effectively available in the
     *            simulation.
     */
    public void setLocals(int locals) {
        this.locals = locals;
    }

    /**
     * @return The bounds into which the entities must be randomly placed. If
     *         the method returns null the placement bounds are considered equal
     *         to the simulation bounds.
     */
    public Point3d[] getRandomBounds() {
        return null;
    }

    /**
     * Collect the offset for the splitting over the {@code LocalCoordinator}s.
     * 
     * @param d
     *            The offset as the {@code SpaceManager} calculates it.
     * 
     */
    public void setOffset(double d) {
        offset = d;
    }
}